package com.d2rv.user;

import com.d2rv.collision.CollisionManager;
import com.d2rv.guide.Goal;
import com.d2rv.guide.InstructionManager;
import com.d2rv.model.ArrowModelManager;
import com.d2rv.model.UserModelManager;
import com.d2rv.offscreen.OffScreen3D;
import com.d2rv.settings.Settings;
import processing.core.PApplet;
import processing.core.PVector;

/**
 * This class is the manager of the class User which controls a user.
 *
 * @author Darrieutort A., Dupouy A., Rossignol L., Van Poucke A.
 */
public class UserManager {

    private User user;
    private CollisionManager collisionmanager;
    private ArrowModelManager arrowmodelmanager;
    private UserModelManager usermodelmanager;
    private InstructionManager instructionmanager;
    private float anglerotate;

    /**
     * Default constructor.
     *
     * @param parent the current application
     */
    public UserManager(PApplet parent) {
        Settings settings = Settings.getInstance();
        this.user = new User(parent, new PVector(-settings.userrecoil(), 0, 0), new PVector(1, 0), new PVector(10000f, 0, 0));
        this.collisionmanager = new CollisionManager(parent);
        this.arrowmodelmanager = new ArrowModelManager(parent);
        this.usermodelmanager = new UserModelManager(parent);
        this.instructionmanager = new InstructionManager(parent);
        this.anglerotate = 0;
    }

    /**
     * Returns the user.
     *
     * @return the user
     */
    public User user() {
        return this.user;
    }

    /**
     * Returns the instruction manager
     *
     * @return the instruction manager
     */
    public InstructionManager instructionmanager() {
        return this.instructionmanager;
    }

    /**
     * Updates the speed, the target and the angle of rotation of the user.
     *
     * @param angle angle of rotation
     */
    public void rotation(float angle) {
        float sx = (float) (this.user.speed().x * Math.cos(angle) - this.user.speed().y * Math.sin(angle));
        float sy = (float) (this.user.speed().x * Math.sin(angle) + this.user.speed().y * Math.cos(angle));
        float tx = (float) (this.user.target().x * Math.cos(angle) - this.user.target().z * Math.sin(angle));
        float tz = (float) (this.user.target().x * Math.sin(angle) + this.user.target().z * Math.cos(angle));

        this.user.speed().x = sx;
        this.user.speed().y = sy;
        this.user.target().x = tx;
        this.user.target().z = tz;
        this.user.rotate((float) ((this.user.rotate() - angle) % Math.toRadians(360)));
    }

    /**
     * Moves the user by updating its position and testing the collision with
     * the city and goals.
     *
     * @param move
     * @param goal
     */
    public void move(PVector move, Goal goal) {
        PVector newpos = new PVector(this.user.position().x + (move.x * this.user.speed().x), this.user.position().y, this.user.position().z + (move.z * this.user.speed().y));
        if (!(this.collisionmanager.testCollision(new PVector(newpos.x, newpos.z)))) {
            this.user.position(newpos);
            this.user.target(new PVector(this.user.target().x + (move.x * this.user.speed().x), this.user.target().y, this.user.target().z + (move.z * this.user.speed().y)));
            this.usermodelmanager.usermodel().position(newpos);
        }
        if (goal != null) {
            if (this.usermodelmanager.usermodel().boundingbox().intersect(goal.goalmodel().boundingbox())
                    || goal.goalmodel().boundingbox().intersect(this.usermodelmanager.usermodel().boundingbox())) {
                goal.goalAchieved();
            }
        }
    }

    /**
     * Returns the user position.
     *
     * @return the user position
     */
    public PVector positionUser() {
        return this.user.position();
    }

    /**
     * Updates the position of the user head.
     *
     * @param move the new position of the head
     */
    public void moveHead(PVector move) {
        this.user.headposition(move);
    }

    /**
     * Draws the user.
     *
     * @param offscreen the current offscreen
     */
    public void drawUser(OffScreen3D offscreen) {
        if(this.anglerotate > 1) {
            this.rotation((float)(Math.toRadians(2)));
            this.anglerotate -= 2;
        }
        this.user.draw(offscreen, this.usermodelmanager.usermodel());
    }

    /**
     * Draws the helping arrow of the user.
     *
     * @param offscreen the current offscreen
     * @param goal the current goal
     */
    public void drawArrowUser(OffScreen3D offscreen, Goal goal) {
        this.user.drawArrow(offscreen, goal, this.arrowmodelmanager.arrowmodel());
    }

    /**
     * Draws the guide instruction in front of the user.
     *
     * @param parent the current offscreen
     */
    public void drawInstruction(OffScreen3D parent) {
        this.user.drawInstruction(parent, this.instructionmanager.currentInstrution());
    }
    
    /**
     * Angle uses to turn back
     * @param angle 
     */
    public void anglerotate(float angle) {
        this.anglerotate = angle;
    }
}